#!/usr/bin/env python

DEFAULT_BOX         = 'default_box'
MAX_COMMENT         = 512

PAGE_UNIT           = 3
PAGE_NUM            = 0

USER_NICKNAME_MAX   = 256
USER_UA_MAX         = 256
USER_URL_MAX        = 256
TITLE_MAX           = 1024
CONTENT_MAX         = 1024 * 64 # 64KB


import math
import random
import sys
import uuid

from google.appengine.ext import db
from google.appengine.api import users
from google.appengine.ext.db import polymodel


class MessageCounter(db.Model):
    uid             = db.StringProperty()
    box             = db.StringProperty()        
    category        = db.StringProperty()
    count           = db.IntegerProperty()

class Message(db.Model):
    uid             = db.StringProperty()
    box             = db.StringProperty(required = True)
    category        = db.StringProperty()
    root_post       = db.BooleanProperty(required = True)

    created_date    = db.DateTimeProperty(auto_now_add = True)
    modified_date   = db.DateTimeProperty(auto_now_add = True)
    title           = db.StringProperty()
    content         = db.TextProperty()

    message_status  = db.StringProperty()   # PUBLIC, DELETED
    comment_status  = db.StringProperty()   # OPENED, CLOSED, FULL

    author_user     = db.UserProperty(required = True)
    author_nickname = db.StringProperty()
    author_ip       = db.StringProperty()
    author_ua       = db.StringProperty()   # User-Agent
    author_url      = db.StringProperty()

    def set_comment_count(self):
        self.comment_count  = 0
        key_name            = 'uid\t%s' % self.uid
        message_counter     = MessageCounter.get_by_key_name(key_name)
        if message_counter:
            if message_counter.count:
                self.comment_count = message_counter.count
        if 1 < self.comment_count:
            self.comment_count_gt1 = True
        else:
            self.comment_count_gt1 = False

def post_add(message_properties):
    def do_message(mp):
        if mp.get('root_post', False):
            # POST_EDIT
            q = db.Query(Message)
            q.filter('uid = ', mp['uid'])
            q.filter('root_post = ', True)
            messages = q.fetch(1)
            if messages:
                message = messages[0]
            else:
                return None

        else:
            # POST_ADD, COMMENT_ADD
            if mp.get('uid', None):
                key_name = 'uid\t%s' % mp['uid']
                message_counter = MessageCounter.get_by_key_name(key_name)
                if message_counter:
                    if MAX_COMMENT <= message_counter.count:
                        return None
            message = Message(
                box         = mp['box'] if mp.get('box', None) else DEFAULT_BOX,
                author_user = mp['author_user'],
                root_post   = False if mp.get('uid', None) else True)

        message.uid             = mp['uid'] if mp.get('uid', None) else uuid.uuid1().hex
        message.category        = mp['category'] if mp.get('category', None) else None
        message.title           = mp.get('title', '')[:TITLE_MAX]
        message.content         = mp.get('content', '')[:CONTENT_MAX]
        message.message_status  = mp.get('message_status', None)
        message.comment_status  = mp.get('comment_status', None)
        message.author_user     = mp['author_user']
        message.author_nickname = mp['author_nickname'][:USER_NICKNAME_MAX] if mp.get('author_nickname', None) else None
        message.author_ip       = mp['author_ip'] if mp.get('author_ip', None) else None
        message.author_ua       = mp['author_ua'][:USER_UA_MAX] if mp.get('author_ua', None) else None
        message.author_url      = mp['author_url'][:USER_URL_MAX] if mp.get('author_url', None) else None
        message.put()
        return message

    def txn_messagecounter(uid, box, category):
        if uid:
            key_name = 'uid\t%s' % uid
        else:
            key_name = 'box,category\t%s,%s' % (box, category)

        message_counter = MessageCounter.get_by_key_name(key_name)
        if not message_counter:
            message_counter = MessageCounter(key_name = key_name)
        if uid:
            message_counter.uid = uid
        message_counter.box = box
        message_counter.category = category
        if message_counter.count:
            message_counter.count += 1
        else:
            message_counter.count = 1
        message_counter.put()

    message = do_message(message_properties)
    if message:
        uid       = message_properties.get('uid', None)
        box       = message.box
        category  = message.category
        root_post = message_properties.get('root_post', False)
        if not root_post:
            db.run_in_transaction(txn_messagecounter, uid, box, category)

def post_list(
        box, category,
        page_unit = PAGE_UNIT, cur_page = PAGE_NUM, 
        reverse = True):
    q = db.Query(Message)
    q.filter('root_post = ', True)
    q.filter('box = ', box)
    q.filter('category = ', category)
    if reverse:
        q.order('-created_date')
    else:
        q.order('created_date')

    total     = post_total(box, category)
    _n        = int(math.ceil(total / float(page_unit)))
    cur_page  = min(_n, max(1, cur_page)) if cur_page else _n
    next_page = 0
    prev_page = 0
    offset    = page_unit * (max(0, cur_page - 1 - 1))
    limit     = (page_unit * 2) if cur_page == 1 else (page_unit * 3)
    messages  = q.fetch(offset = offset, limit = limit)
    messages_len = len(messages)
    offset_aj = 0 if cur_page == 1 else page_unit * (min(2, int(math.ceil(messages_len / float(page_unit)))) - 1)
    messages_ret = messages[offset_aj : offset_aj + page_unit]
    messages_ret.reverse()

    for message in messages_ret:
        message.set_comment_count()
    # XXX: we ckeck whether the next or previous page exists only 
    #      for the range of page_unit.
    for message in messages[:offset_aj]:
        if message.message_status == 'PUBLIC':
            prev_page = cur_page - 1
            break
    for message in messages[offset_aj + page_unit:]:
        if message.message_status == 'PUBLIC':
            next_page = cur_page + 1
            break

    return (messages_ret, prev_page, cur_page, next_page)

def post_read(box, uid):
    q = db.Query(Message)
    q.filter('box = ', box)
    q.filter('uid = ', uid)
    q.order('created_date')
    messages = q.fetch(limit = MAX_COMMENT + 1)
    if messages:
        messages[0].set_comment_count()
    return messages

def post_total(box, category):
    key_name = 'box,category\t%s,%s' % (box, category)
    message_counter = MessageCounter.get_by_key_name(key_name)
    if message_counter:
        return message_counter.count
    else:
        return 0

def comment_recent_list(box, category, limit):
    q = db.Query(Message)
    q.filter('box = ', box)
    q.filter('category = ', category)
    q.filter('root_post = ', False)
    q.order('-created_date')
    messages = q.fetch(limit)
    for message in messages:
        message.content_length = len(message.content)
        message.content_gt24 = True if 24 < message.content_length else False
    return messages

